Passed
Push — master ( a3ce85...9dd71e )
by EMP
01:40
created

main.js ➔ getRowsPerPage   A

Complexity

Conditions 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 11
rs 9.95
c 0
b 0
f 0
cc 1
1
"use strict";
2
3
sodium.ready.then(function() {
4
5
const ae = new AllEars(function(ok) {
6
	if (ok) {
7
		if (localStorage.greeting) {
8
			document.getElementById("greeting").textContent = localStorage.greeting;
9
			document.getElementById("txt_pg").value = localStorage.greeting;
10
		} else localStorage.greeting = document.getElementById("greeting").textContent;
11
12
		document.getElementById("txt_skey").style.background = "#466";
13
		document.getElementById("txt_skey").maxLength = "64";
14
	} else {
15
		console.log("Failed to load All-Ears");
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
16
	}
17
});
18
19
function TabState(cur, max, btnDele, btnUpdt) {
20
	this.cur = cur;
21
	this.max = max;
22
	this.btnDele = btnDele;
23
	this.btnUpdt = btnUpdt;
24
}
25
26
const tabs = [
27
	new TabState(0, 0, false, true), // Inbox
28
	new TabState(0, 0, false, true), // Outbx
29
	new TabState(0, 1, true, false), // Write
30
	new TabState(0, 2, false, false), // Notes
31
	new TabState(0, 2, false, true) // Tools
32
];
33
34
let showHeaders = false;
35
36
let tab = 0;
37
const TAB_INBOX = 0;
38
const TAB_DRBOX = 1;
39
const TAB_WRITE = 2;
40
const TAB_NOTES = 3;
41
const TAB_TOOLS = 4;
42
43
// Helper functions
44
function getCountryName(countryCode) {
45
	switch (countryCode) {
46
		case "??": return "Unknown";
47
		case "DZ": return "Algeria";
48
		case "AO": return "Angola";
49
		case "BJ": return "Benin";
50
		case "BW": return "Botswana";
51
		case "BF": return "Burkina Faso";
52
		case "BI": return "Burundi";
53
		case "CV": return "Cabo Verde";
54
		case "CM": return "Cameroon";
55
		case "CF": return "Central African Republic";
56
		case "TD": return "Chad";
57
		case "KM": return "Comoros";
58
		case "CD": return "Congo";
59
		case "DJ": return "Djibouti";
60
		case "EG": return "Egypt";
61
		case "GQ": return "Equatorial Guinea";
62
		case "ER": return "Eritrea";
63
		case "SZ": return "Eswatini";
64
		case "ET": return "Ethiopia";
65
		case "GA": return "Gabon";
66
		case "GM": return "Gambia";
67
		case "GH": return "Ghana";
68
		case "GW": return "Guinea-Bissau";
69
		case "GN": return "Guinea";
70
		case "CI": return "Ivory Coast";
71
		case "KE": return "Kenya";
72
		case "LS": return "Lesotho";
73
		case "LR": return "Liberia";
74
		case "LY": return "Libya";
75
		case "MG": return "Madagascar";
76
		case "MW": return "Malawi";
77
		case "ML": return "Mali";
78
		case "MR": return "Mauritania";
79
		case "MU": return "Mauritius";
80
		case "YT": return "Mayotte";
81
		case "MA": return "Morocco";
82
		case "MZ": return "Mozambique";
83
		case "NA": return "Namibia";
84
		case "NE": return "Niger";
85
		case "NG": return "Nigeria";
86
		case "CG": return "Republic of the Congo";
87
		case "RW": return "Rwanda";
88
		case "RE": return "Réunion";
89
		case "SH": return "Saint Helena";
90
		case "SN": return "Senegal";
91
		case "SC": return "Seychelles";
92
		case "SL": return "Sierra Leone";
93
		case "SO": return "Somalia";
94
		case "ZA": return "South Africa";
95
		case "SS": return "South Sudan";
96
		case "SD": return "Sudan";
97
		case "ST": return "São Tomé and Príncipe";
98
		case "TZ": return "Tanzania";
99
		case "TG": return "Togo";
100
		case "TN": return "Tunisia";
101
		case "UG": return "Uganda";
102
		case "EH": return "Western Sahara";
103
		case "ZM": return "Zambia";
104
		case "ZW": return "Zimbabwe";
105
		case "AQ": return "Antarctica";
106
		case "BV": return "Bouvet Island";
107
		case "TF": return "French Southern Territories";
108
		case "HM": return "Heard Island and McDonald Islands";
109
		case "GS": return "South Georgia and the South Sandwich Islands";
110
		case "AF": return "Afghanistan";
111
		case "AM": return "Armenia";
112
		case "AZ": return "Azerbaijan";
113
		case "BH": return "Bahrain";
114
		case "BD": return "Bangladesh";
115
		case "BT": return "Bhutan";
116
		case "IO": return "British Indian Ocean Territory";
117
		case "BN": return "Brunei";
118
		case "KH": return "Cambodia";
119
		case "CN": return "China";
120
		case "CC": return "Cocos [Keeling] Islands";
121
		case "GE": return "Georgia";
122
		case "JO": return "Hashemite Kingdom of Jordan";
123
		case "HK": return "Hong Kong";
124
		case "IN": return "India";
125
		case "ID": return "Indonesia";
126
		case "IR": return "Iran";
127
		case "IQ": return "Iraq";
128
		case "IL": return "Israel";
129
		case "JP": return "Japan";
130
		case "KZ": return "Kazakhstan";
131
		case "KW": return "Kuwait";
132
		case "KG": return "Kyrgyzstan";
133
		case "LA": return "Laos";
134
		case "LB": return "Lebanon";
135
		case "MO": return "Macao";
136
		case "MY": return "Malaysia";
137
		case "MV": return "Maldives";
138
		case "MN": return "Mongolia";
139
		case "MM": return "Myanmar";
140
		case "NP": return "Nepal";
141
		case "KP": return "North Korea";
142
		case "OM": return "Oman";
143
		case "PK": return "Pakistan";
144
		case "PS": return "Palestine";
145
		case "PH": return "Philippines";
146
		case "QA": return "Qatar";
147
		case "SA": return "Saudi Arabia";
148
		case "SG": return "Singapore";
149
		case "KR": return "South Korea";
150
		case "LK": return "Sri Lanka";
151
		case "SY": return "Syria";
152
		case "TW": return "Taiwan";
153
		case "TJ": return "Tajikistan";
154
		case "TH": return "Thailand";
155
		case "TR": return "Turkey";
156
		case "TM": return "Turkmenistan";
157
		case "AE": return "United Arab Emirates";
158
		case "UZ": return "Uzbekistan";
159
		case "VN": return "Vietnam";
160
		case "YE": return "Yemen";
161
		case "AL": return "Albania";
162
		case "AD": return "Andorra";
163
		case "AT": return "Austria";
164
		case "BY": return "Belarus";
165
		case "BE": return "Belgium";
166
		case "BA": return "Bosnia and Herzegovina";
167
		case "BG": return "Bulgaria";
168
		case "HR": return "Croatia";
169
		case "CY": return "Cyprus";
170
		case "CZ": return "Czechia";
171
		case "DK": return "Denmark";
172
		case "EE": return "Estonia";
173
		case "FO": return "Faroe Islands";
174
		case "FI": return "Finland";
175
		case "FR": return "France";
176
		case "DE": return "Germany";
177
		case "GI": return "Gibraltar";
178
		case "GR": return "Greece";
179
		case "GG": return "Guernsey";
180
		case "HU": return "Hungary";
181
		case "IS": return "Iceland";
182
		case "IE": return "Ireland";
183
		case "IM": return "Isle of Man";
184
		case "IT": return "Italy";
185
		case "JE": return "Jersey";
186
		case "XK": return "Kosovo";
187
		case "LV": return "Latvia";
188
		case "LI": return "Liechtenstein";
189
		case "LU": return "Luxembourg";
190
		case "MT": return "Malta";
191
		case "MC": return "Monaco";
192
		case "ME": return "Montenegro";
193
		case "NL": return "Netherlands";
194
		case "MK": return "North Macedonia";
195
		case "NO": return "Norway";
196
		case "PL": return "Poland";
197
		case "PT": return "Portugal";
198
		case "LT": return "Republic of Lithuania";
199
		case "MD": return "Republic of Moldova";
200
		case "RO": return "Romania";
201
		case "RU": return "Russia";
202
		case "SM": return "San Marino";
203
		case "RS": return "Serbia";
204
		case "SK": return "Slovakia";
205
		case "SI": return "Slovenia";
206
		case "ES": return "Spain";
207
		case "SJ": return "Svalbard and Jan Mayen";
208
		case "SE": return "Sweden";
209
		case "CH": return "Switzerland";
210
		case "UA": return "Ukraine";
211
		case "GB": return "United Kingdom";
212
		case "VA": return "Vatican City";
213
		case "AX": return "Åland";
214
		case "AI": return "Anguilla";
215
		case "AG": return "Antigua and Barbuda";
216
		case "AW": return "Aruba";
217
		case "BS": return "Bahamas";
218
		case "BB": return "Barbados";
219
		case "BZ": return "Belize";
220
		case "BM": return "Bermuda";
221
		case "BQ": return "Bonaire, Sint Eustatius, and Saba";
222
		case "VG": return "British Virgin Islands";
223
		case "CA": return "Canada";
224
		case "KY": return "Cayman Islands";
225
		case "CR": return "Costa Rica";
226
		case "CU": return "Cuba";
227
		case "CW": return "Curaçao";
228
		case "DM": return "Dominica";
229
		case "DO": return "Dominican Republic";
230
		case "SV": return "El Salvador";
231
		case "GL": return "Greenland";
232
		case "GD": return "Grenada";
233
		case "GP": return "Guadeloupe";
234
		case "GT": return "Guatemala";
235
		case "HT": return "Haiti";
236
		case "HN": return "Honduras";
237
		case "JM": return "Jamaica";
238
		case "MQ": return "Martinique";
239
		case "MX": return "Mexico";
240
		case "MS": return "Montserrat";
241
		case "NI": return "Nicaragua";
242
		case "PA": return "Panama";
243
		case "PR": return "Puerto Rico";
244
		case "BL": return "Saint Barthélemy";
245
		case "LC": return "Saint Lucia";
246
		case "MF": return "Saint Martin";
247
		case "PM": return "Saint Pierre and Miquelon";
248
		case "VC": return "Saint Vincent and the Grenadines";
249
		case "SX": return "Sint Maarten";
250
		case "KN": return "St Kitts and Nevis";
251
		case "TT": return "Trinidad and Tobago";
252
		case "TC": return "Turks and Caicos Islands";
253
		case "VI": return "U.S. Virgin Islands";
254
		case "US": return "United States";
255
		case "AS": return "American Samoa";
256
		case "AU": return "Australia";
257
		case "CX": return "Christmas Island";
258
		case "CK": return "Cook Islands";
259
		case "TL": return "Democratic Republic of Timor-Leste";
260
		case "FM": return "Federated States of Micronesia";
261
		case "FJ": return "Fiji";
262
		case "PF": return "French Polynesia";
263
		case "GU": return "Guam";
264
		case "KI": return "Kiribati";
265
		case "MH": return "Marshall Islands";
266
		case "NR": return "Nauru";
267
		case "NC": return "New Caledonia";
268
		case "NZ": return "New Zealand";
269
		case "NU": return "Niue";
270
		case "NF": return "Norfolk Island";
271
		case "MP": return "Northern Mariana Islands";
272
		case "PW": return "Palau";
273
		case "PG": return "Papua New Guinea";
274
		case "PN": return "Pitcairn Islands";
275
		case "WS": return "Samoa";
276
		case "SB": return "Solomon Islands";
277
		case "TK": return "Tokelau";
278
		case "TO": return "Tonga";
279
		case "TV": return "Tuvalu";
280
		case "UM": return "U.S. Minor Outlying Islands";
281
		case "VU": return "Vanuatu";
282
		case "WF": return "Wallis and Futuna";
283
		case "AR": return "Argentina";
284
		case "BO": return "Bolivia";
285
		case "BR": return "Brazil";
286
		case "CL": return "Chile";
287
		case "CO": return "Colombia";
288
		case "EC": return "Ecuador";
289
		case "FK": return "Falkland Islands";
290
		case "GF": return "French Guiana";
291
		case "GY": return "Guyana";
292
		case "PY": return "Paraguay";
293
		case "PE": return "Peru";
294
		case "SR": return "Suriname";
295
		case "UY": return "Uruguay";
296
		case "VE": return "Venezuela";
297
	}
298
299
	return "Error";
300
}
301
302
function getCountryFlag(countryCode) {
303
	return (!countryCode || countryCode.length !== 2 || countryCode == "??") ? "❔" : sodium.to_string(new Uint8Array([
304
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
305
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
306
	]));
307
}
308
309
function getClockIcon(d) {
310
	const h24 = d.getUTCHours();
311
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
312
313
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
314
	let m30 = 0;
315
	if (m60 <= 900) { // <= 15: round down to this hour
316
		m30 = 0;
317
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
318
		m30 = 12;
319
	} else { // >= 45: round up to next hour
320
		h12++;
321
		m30 = 0;
322
	}
323
324
	return "&#" + ((128335 + h12) + m30) + ";";
325
}
326
327
function clearDisplay() {
328
	let      el = document.querySelector("article > img");
329
	if (!el) el = document.querySelector("article > audio");
330
	if (!el) el = document.querySelector("article > video");
331
	if (!el) el = document.querySelector("article > embed");
332
	if (!el) el = document.querySelector("article > iframe");
333
	if (!el) return;
334
335
	URL.revokeObjectURL(el.src);
336
	el.remove();
337
}
338
339
function downloadFile(num) {
340
	const a = document.createElement("a");
341
	a.href = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
342
	a.download = ae.GetUplMsgTitle(num);
343
	a.click();
344
345
	URL.revokeObjectURL(a.href);
346
	a.href = "";
347
	a.download = "";
348
}
349
350
function displayFile(num) {
351
	const fileType = ae.GetUplMsgType(num);
352
	if (!fileType) {downloadFile(num); return;}
353
354
	clearDisplay();
355
	document.querySelector("article").scroll(0, 0);
356
	document.querySelector("article").setAttribute("data-msgid", ae.GetUplMsgIdHex(num));
357
358
	document.getElementById("btn_mdele").disabled = false;
359
	document.getElementById("btn_msave").disabled = false;
360
	document.getElementById("btn_reply").disabled = true;
361
362
	document.getElementById("btn_msave").onclick = function() {downloadFile(num);};
363
364
	document.querySelector("article").children[0].hidden = true;
365
	document.querySelector("article").children[1].textContent = ae.GetUplMsgTitle(num);
366
367
	switch (fileType) {
368
		case "text": {
369
			document.querySelector("article").children[2].hidden = false;
370
			document.querySelector("article").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
371
		break;}
372
373
		case "image": {
374
			document.querySelector("article").children[2].hidden = true;
375
			const img = document.createElement("img");
376
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
377
			document.querySelector("article").appendChild(img);
378
379
			img.onclick = function() {
380
				if (!document.fullscreen)
381
					img.requestFullscreen();
382
				else
383
					document.exitFullscreen();
384
			};
385
		break;}
386
387
		case "audio":
388
		case "video": {
389
			document.querySelector("article").children[2].hidden = true;
390
			const el = document.createElement(fileType);
391
			el.controls = "controls";
392
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
393
			document.querySelector("article").appendChild(el);
394
		break;}
395
396
		case "pdf": {
397
			document.querySelector("article").children[2].hidden = true;
398
			const el = document.createElement("embed");
399
			el.type = "application/pdf";
400
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "application/pdf"}));
401
			document.querySelector("article").appendChild(el);
402
		break;}
403
404
		case "html": {
405
			document.querySelector("article").children[2].hidden = true;
406
			const el = document.createElement("iframe");
407
			el.allow = "";
408
			el.sandbox = "";
409
			el.referrerPolicy = "no-referrer";
410
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
411
			el.srcdoc = sodium.to_string(ae.GetUplMsgBody(num).buffer);
412
			document.querySelector("article").appendChild(el);
413
		break;}
414
	}
415
}
416
417
function displayMsg(isInt, num) {
418
	clearDisplay();
419
420
	document.getElementById("btn_mdele").disabled = false;
421
	document.getElementById("btn_msave").disabled = isInt;
422
423
	document.querySelector("article").scroll(0, 0);
424
	document.querySelector("article").setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num));
425
426
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
427
428
	if (!isInt) {
429
		document.getElementById("btn_msave").onclick = function() {
430
			this.blur();
431
432
			const a = document.createElement("a");
433
			a.href = URL.createObjectURL(new Blob([ae.ExportExtMsg(num)]));
434
			a.download = ae.GetExtMsgTitle(num);
435
			a.click();
436
437
			URL.revokeObjectURL(a.href);
438
			a.href = "";
439
			a.download = "";
440
		};
441
	}
442
443
	if (!isInt || (ae.GetIntMsgFrom(num) !== "public" && ae.GetIntMsgFrom(num) !== "system")) {
444
		document.getElementById("btn_reply").disabled = false;
445
446
		document.getElementById("btn_reply").onclick = function() {
447
			document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgReplyAddress(num);
448
			document.getElementById("write_subj").value = isInt? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num);
449
			if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
450
			document.querySelector("#write2_pkey > input").value = isInt? ae.GetIntMsgFromPk(num) : "";
451
452
			document.getElementById("write_recv").readOnly = !isInt;
453
			document.getElementById("write_subj").readOnly = !isInt;
454
			document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : ae.GetExtMsgHdrId(num));
455
456
			tabs[TAB_WRITE].cur = 0;
457
			document.getElementById("btn_write").disabled = false;
458
			document.getElementById("btn_write").click();
459
			document.getElementById("write_body").focus();
460
461
			for (const opt of document.getElementById("write_from").options) {
462
				if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgEnvTo(num).split("@")[0].toLowerCase())) {
463
					opt.selected = true;
464
				}
465
			}
466
		};
467
	} else {
468
		document.getElementById("btn_reply").disabled = true;
469
	}
470
471
	document.querySelector("article").children[0].hidden = false;
472
	document.querySelector("article").children[2].hidden = false;
473
474
	document.getElementById("readmsg_envto").textContent = isInt ? "" : ae.GetExtMsgEnvTo(num);
475
	document.getElementById("readmsg_hdrto").textContent = isInt ? ae.GetIntMsgTo(num) : (ae.GetExtMsgHdrTo(num) + (ae.GetExtMsgDnTo(num) ? " (" + ae.GetExtMsgDnTo(num) + ")" : ""));
476
477
	const tzOs = new Date().getTimezoneOffset();
478
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
479
	document.getElementById("readmsg_date").children[0].innerHTML = getClockIcon(msgDate);
480
	document.getElementById("readmsg_date").children[1].dateTime = new Date(ts * 1000).toISOString();
481
482
	if (isInt) {
483
		document.querySelector("article").children[1].textContent = ae.GetIntMsgTitle(num);
484
		document.querySelector("article").children[2].textContent = ae.GetIntMsgBody(num);
485
486
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
487
488
		document.getElementById("readmsg_ip").style.visibility = "hidden";
489
		document.getElementById("readmsg_rdns").style.visibility = "hidden";
490
		document.getElementById("readmsg_dkim").style.visibility = "hidden";
491
		document.getElementById("readmsg_greet").style.visibility = "hidden";
492
		document.getElementById("readmsg_cert").style.visibility = "hidden";
493
		document.getElementById("readmsg_envfrom").style.visibility = "hidden";
494
		document.getElementById("readmsg_envto").style.visibility = "hidden";
495
496
		if (ae.GetIntMsgFrom(num) !== "system" && ae.GetIntMsgFrom(num) !== "public") {
497
			document.getElementById("readmsg_tls").style.visibility = "visible";
498
			document.getElementById("readmsg_tls").children[0].textContent = ae.GetIntMsgFromPk(num);
499
		} else document.getElementById("readmsg_tls").style.visibility = "hidden";
500
501
		let symbol = "<span title=\"Invalid level\">&#x26a0;</span>";
502
		if      (ae.GetIntMsgFrom(num) === "system") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"System message\">&#x1f162;</span>";} // (S)
503
		else if (ae.GetIntMsgFrom(num) === "public") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Public announcement\">&#x1f15f;</span>";} // (P)
504
		else if (ae.GetIntMsgLevel(num) === 0) symbol = "<span title=\"Level 0 User\">&#x1f10c;</span>"; // 0
505
		else if (ae.GetIntMsgLevel(num) === 1) symbol = "<span title=\"Level 1 User\">&#x278a;</span>"; // 1
506
		else if (ae.GetIntMsgLevel(num) === 2) symbol = "<span title=\"Level 2 User\">&#x278b;</span>"; // 2
507
		else if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Administrator\">&#x1f150;</span>"; // A (Admin)
508
		document.getElementById("readmsg_hdrfrom").innerHTML = symbol + " " + ae.GetIntMsgFrom(num);
509
510
		let flagText = "";
511
		if (!ae.GetIntMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
512
		if (!ae.GetIntMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
513
		if (ae.GetIntMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
514
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
515
	} else {
516
		document.querySelector("article").children[2].innerHTML = "";
517
518
		const headers = document.createElement("p");
519
		headers.textContent = ae.GetExtMsgHeaders(num);
520
		headers.className = "mono";
521
		headers.hidden = !showHeaders;
522
		document.querySelector("article").children[2].appendChild(headers);
523
524
		const body = document.createElement("p");
525
		body.innerHTML = ae.GetExtMsgBody(num);
526
		document.querySelector("article").children[2].appendChild(body);
527
528
		document.querySelector("article").children[1].textContent = ae.GetExtMsgTitle(num);
529
		document.querySelector("article").children[1].style.cursor = headers.textContent? "pointer" : "";
530
		document.querySelector("article").children[1].onclick = function() {
531
			if (!headers.textContent) return;
532
			showHeaders = !showHeaders;
533
			headers.hidden = !showHeaders;
534
		};
535
536
		let hdrSecs = Math.abs(ae.GetExtMsgHdrTime(num));
537
		let hdrTime = "";
538
		if (hdrSecs >= 3600) {
539
			const hdrHours = Math.floor(hdrSecs / 3600);
540
			hdrTime += hdrHours.toString() + "h ";
541
			hdrSecs -= hdrHours * 3600;
542
		}
543
		if (hdrSecs >= 60) {
544
			const hdrMins = Math.floor(hdrSecs / 60);
545
			hdrTime += hdrMins.toString() + "m ";
546
			hdrSecs -= hdrMins * 60;
547
		}
548
		hdrTime += hdrSecs + "s";
549
550
		const hdrTz = (ae.GetExtMsgHdrTz(num) >= 0 ? "+" : "-") + Math.floor(Math.abs(ae.GetExtMsgHdrTz(num)) / 60).toString().padStart(2, "0") + (Math.abs(ae.GetExtMsgHdrTz(num)) % 60).toString().padStart(2, "0");
551
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ") + "; " + hdrTz + " " + ((ae.GetExtMsgHdrTime(num) >= 0) ? "+" : "-") + hdrTime;
552
553
		document.getElementById("readmsg_ip").style.visibility = "visible";
554
		document.getElementById("readmsg_rdns").style.visibility = "visible";
555
		document.getElementById("readmsg_dkim").style.visibility = "visible";
556
		document.getElementById("readmsg_greet").style.visibility = "visible";
557
		document.getElementById("readmsg_tls").style.visibility = "visible";
558
		document.getElementById("readmsg_cert").style.visibility = "visible";
559
		document.getElementById("readmsg_envfrom").style.visibility = "visible";
560
		document.getElementById("readmsg_envto").style.visibility = "visible";
561
562
		const cc = ae.GetExtMsgCountry(num);
563
564
		// DKIM
565
		let dkim = "";
566
		if (ae.GetExtMsgDkim(num)) {
567
			[ // Look for a matching domain in this order
568
				ae.GetExtMsgHdrFrom(num).split("@")[1],
569
				ae.GetExtMsgEnvFrom(num).split("@")[1],
570
				ae.GetExtMsgRdns(num),
571
				ae.GetExtMsgGreet(num),
572
				ae.GetExtMsgTlsDomain(num)
573
			].forEach(function(dom) {
574
				if (dkim) return;
575
				for (let i = 0; i < ae.GetExtMsgDkim(num).domain.length; i++) {
576
					if (ae.GetExtMsgDkim(num).domain[i] === dom) {
577
						dkim = dom + " ✓";
578
						return;
579
					}
580
				}
581
			});
582
583
			if (!dkim) dkim = ae.GetExtMsgDkim(num).domain[0]; // Default to first signature domain
584
		}
585
586
		if (ae.GetExtMsgFlagDkFl(num)) dkim += " (fail)";
587
588
		// Left side
589
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc);
590
		document.getElementById("readmsg_country").title = getCountryName(cc);
591
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetExtMsgIp(num) + (ae.GetExtMsgFlagIpBl(num) ? " ❗" : "");
592
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
593
594
		// Right side
595
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num) + (ae.GetExtMsgFlagGrDm(num) ? " ✓" : "");
596
		document.getElementById("readmsg_rdns").children[0].textContent = ae.GetExtMsgRdns(num) + (ae.GetExtMsgGreet(num) === ae.GetExtMsgRdns(num) ? " ✓" : "");
597
		document.getElementById("readmsg_cert").children[0].textContent = ae.GetExtMsgTlsDomain(num) ? (ae.GetExtMsgTlsDomain(num) + " ✓") : "";
598
		document.getElementById("readmsg_dkim").children[0].textContent = dkim;
599
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgEnvFrom(num);
600
		document.getElementById("readmsg_hdrfrom").textContent = ae.GetExtMsgHdrFrom(num) + (ae.GetExtMsgDnFrom(num) ? " (" + ae.GetExtMsgDnFrom(num) + ")" : "");
601
602
		let flagText = "";
603
		if (!ae.GetExtMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
604
		if (!ae.GetExtMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
605
		if (!ae.GetExtMsgFlagPExt(num)) flagText += "<abbr title=\"The sender did not use the Extended (ESMTP) protocol\">SMTP</abbr> ";
606
		if (!ae.GetExtMsgFlagQuit(num)) flagText += "<abbr title=\"The sender did not issue the required QUIT command\">QUIT</abbr> ";
607
		if (ae.GetExtMsgFlagRare(num)) flagText += "<abbr title=\"The sender issued unusual command(s)\">RARE</abbr> ";
608
		if (ae.GetExtMsgFlagFail(num)) flagText += "<abbr title=\"The sender issued invalid command(s)\">FAIL</abbr> ";
609
		if (ae.GetExtMsgFlagPErr(num)) flagText += "<abbr title=\"The sender violated the protocol\">PROT</abbr> ";
610
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
611
	}
612
}
613
614
function getErrorMessage(err) {
615
	switch (err) {
616
		// 0x01-0x20	Client-side error codes
617
		case 0x01: return "Invalid input";
618
		case 0x02: return "Only administrators can perform this action";
619
		case 0x03: return "_FetchEncrypted: Invalid input";
620
		case 0x04: return "_FetchEncrypted: Failed connecting to server";
621
		case 0x05: return "_FetchEncrypted: Failed decrypting response";
622
		case 0x06: return "_FetchEncrypted: Invalid response length";
623
		case 0x07: return "Invalid response from server";
624
		case 0x08: return "Addr32 encoding failed";
625
626
		case 0x10: return "Message too short";
627
		case 0x11: return "Name too long";
628
		case 0x12: return "File too large";
629
630
		// 0x21-0x26	Generic
631
		case 0x21: return ["FORMAT",    "Invalid format"];
632
		case 0x22: return ["ADMINONLY", "Only administrators can perform this action"];
633
		case 0x23: return ["MISC",      "Unknown error"];
634
		case 0x24: return ["INTERNAL",  "Internal server error"];
635
		case 0x25: return ["TODO",      "Functionality missing - in development"];
636
		case 0x26: return ["FIXME",     "Unexpected error encountered"];
637
638
		// 0xE0-0xEF	Message/Create
639
		case 0xE0: return ["MESSAGE_CREATE_EXT_MINLEVEL",        "Account level too low"];
640
		case 0xE1: return ["MESSAGE_CREATE_EXT_FORMAT_FROM",     "Malformed from-address"];
641
		case 0xE2: return ["MESSAGE_CREATE_EXT_FORMAT_TO",       "Malformed to-address"];
642
		case 0xE3: return ["MESSAGE_CREATE_EXT_FORMAT_REPLYID",  "Malformed reply-id"];
643
		case 0xE4: return ["MESSAGE_CREATE_EXT_FORMAT_SUBJECT",  "Malformed subject"];
644
		case 0xE5: return ["MESSAGE_CREATE_EXT_INVALID_REPLYID", "Invalid reply-id"];
645
		case 0xE6: return ["MESSAGE_CREATE_EXT_INVALID_FROM",    "Invalid from-address"];
646
		case 0xE7: return ["MESSAGE_CREATE_EXT_INVALID_TO",      "Invalid to-address"];
647
		case 0xE8: return ["MESSAGE_CREATE_EXT_BODY_SIZE",       "Body too long or short"];
648
		case 0xE9: return ["MESSAGE_CREATE_EXT_BODY_UTF8",       "Body not UTF-8"];
649
		case 0xEA: return ["MESSAGE_CREATE_EXT_BODY_CONTROL",    "Body contains control characters"];
650
		case 0xEB: return ["MESSAGE_CREATE_EXT_LINE_TOOLONG",    "Body exceeds line-length limit"];
651
		case 0xEC: return ["MESSAGE_CREATE_EXT_BODY_FORMAT",     "Malformed body"];
652
		case 0xED: return ["MESSAGE_CREATE_EXT_BODY_TOOSHORT",   "Body too short"];
653
		case 0xEE: return ["MESSAGE_CREATE_EXT_TODOMAIN",        "Invalid to-address domain"];
654
//		case 0xEF: return ["", ""];
655
656
		// 0xF0-0xF9	Message/Create sendMail()
657
		case 0xF0: return ["MESSAGE_CREATE_SENDMAIL_GREET", "Failed greeting receiver server"];
658
		case 0xF1: return ["MESSAGE_CREATE_SENDMAIL_EHLO",  "EHLO command failed"];
659
		case 0xF2: return ["MESSAGE_CREATE_SENDMAIL_STLS",  "STARTTLS command failed"];
660
		case 0xF3: return ["MESSAGE_CREATE_SENDMAIL_SHAKE", "TLS handshake failed"];
661
		case 0xF4: return ["MESSAGE_CREATE_SENDMAIL_NOTLS", "TLS not available"];
662
		case 0xF5: return ["MESSAGE_CREATE_SENDMAIL_MAIL",  "MAIL command failed"];
663
		case 0xF6: return ["MESSAGE_CREATE_SENDMAIL_RCPT",  "RCPT command failed"];
664
		case 0xF7: return ["MESSAGE_CREATE_SENDMAIL_DATA",  "DATA command failed"];
665
		case 0xF8: return ["MESSAGE_CREATE_SENDMAIL_BODY",  "Sending body failed"];
666
//		case 0xF9: return ["", ""];
667
668
		// 0xFA-0xFF	Message/Create Int
669
		case 0xFA: return ["MESSAGE_CREATE_INT_TOOSHORT",     "Message too short"];
670
		case 0xFB: return ["MESSAGE_CREATE_INT_TS_INVALID",   "Invalid timestamp"];
671
		case 0xFC: return ["MESSAGE_CREATE_INT_SUBJECT_SIZE", "Subject too long or short"];
672
		case 0xFD: return ["MESSAGE_CREATE_INT_ADDR_NOTOWN",  "Sender address not owned"];
673
		case 0xFE: return ["MESSAGE_CREATE_INT_TO_NOTACCEPT", "Receiver address does not accept messages"];
674
		case 0xFF: return ["MESSAGE_CREATE_INT_TO_SELF",      "Sending to own account not allowed"];
675
676
		default: return ["???", "Unknown error"];
677
	}
678
}
679
680
// Interface
681
function errorDialog(err) {
682
	if (typeof(err) !== "number" || err < 1) return;
683
684
	let btnDisable = [];
685
	const btn = document.querySelectorAll("nav > button");
686
	for (let i = 0; i < btn.length; i++) {
687
		btnDisable.push(btn[i].disabled);
688
		btn[i].disabled = true;
689
	}
690
691
	const errMsg = getErrorMessage(err);
692
693
	const dlg = document.querySelector("dialog");
694
	dlg.children[0].style.height = getComputedStyle(document.querySelector("#main1 > div[class='mid']")).height;
695
	dlg.querySelector("h1").textContent = "ERROR 0x" + err.toString(16).padStart(2, "0").toUpperCase();
696
	dlg.querySelector("p").textContent = (typeof(errMsg) === "string") ? errMsg : errMsg[1];
697
	dlg.show();
698
699
	document.querySelector("dialog > div").onclick = function() {
700
		for (let i = 0; i < btn.length; i++) {
701
			btn[i].disabled = btnDisable[i];
702
			dlg.close();
703
		}
704
	}
705
}
706
707
function addMsg(isInt, i) {
708
	const row = document.getElementById("tbl_inbox").insertRow(-1);
709
	row.setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(i) : ae.GetExtMsgIdHex(i));
710
711
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
712
	const el = document.createElement("time");
713
	el.dateTime = new Date(ts * 1000).toISOString();
714
	el.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
715
716
	const cellTime = row.insertCell(-1);
717
	cellTime.appendChild(el);
718
719
	const cellSubj = row.insertCell(-1);
720
	cellSubj.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
721
722
	if (isInt) {
723
		const cellSnd = row.insertCell(-1);
724
		cellSnd.textContent = ae.GetIntMsgFrom(i);
725
		cellSnd.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
726
	} else {
727
		const from1 = ae.GetExtMsgHdrFrom(i);
728
		const from2 = from1.substring(from1.indexOf("@") + 1);
729
		const cc = ae.GetExtMsgCountry(i);
730
		const cellSnd1 = row.insertCell(-1);
731
		cellSnd1.textContent = from1.substring(0, from1.indexOf("@"));
732
733
		const flag = document.createElement("abbr");
734
		flag.textContent = getCountryFlag(cc);
735
		flag.title = getCountryName(cc);
736
737
		const fromText = document.createElement("span");
738
		fromText.textContent = " " + from2;
739
740
		const cellSnd2 = row.insertCell(-1);
741
		cellSnd2.appendChild(flag);
742
		cellSnd2.appendChild(fromText);
743
	}
744
745
	row.onclick = function() {
746
		displayMsg(isInt, i);
747
	};
748
}
749
750
function getRowsPerPage() {
751
	const tbl = document.getElementById("tbl_inbox");
752
	tbl.innerHTML = "";
753
	const row = tbl.insertRow(-1);
754
	const cell = row.insertCell(-1);
755
	cell.textContent = "0";
756
757
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
758
	tbl.innerHTML = "";
759
	return rowsPerPage;
760
}
761
762
function addMessages() {
763
	const maxExt = ae.GetExtMsgCount();
764
	const maxInt = ae.GetIntMsgCount();
765
766
	if (maxExt + maxInt < 1) {
767
		tabs[TAB_INBOX].max = 0;
768
		return;
769
	}
770
771
	const rowsPerPage = getRowsPerPage();
772
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
773
774
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - 1) / rowsPerPage);
775
776
	let numExt = 0;
777
	let numInt = 0;
778
	let numAdd = 0;
779
780
	while (numAdd < rowsPerPage) {
781
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
782
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
783
		if (tsInt === -1 && tsExt === -1) break;
784
785
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
786
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
787
			numInt++;
788
		} else if (tsExt !== -1) {
789
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
790
			numExt++;
791
		}
792
	}
793
794
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
795
		const inbox = document.getElementById("tbl_inbox");
796
		const row = inbox.insertRow(-1);
797
		const cell = row.insertCell(-1);
798
		cell.textContent = "Load more (" + Math.round((ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024) + " KiB left)";
799
800
		row.onclick = function() {
801
			this.onclick = "";
802
803
			ae.Message_Browse(false, false, function(errorBrowse) {
804
				document.getElementById("tbl_inbox").style.opacity = 1;
805
806
				if (!errorBrowse) {
807
					addMessages();
808
					addUploads();
809
					addSent();
810
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
811
				} // else TODO
812
			});
813
		};
814
	}
815
}
816
817
function addUploads() {
818
	const tbl = document.getElementById("tbd_uploads");
819
	tbl.innerHTML = "";
820
821
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
822
		const row = tbl.insertRow(-1);
823
		row.setAttribute("data-msgid", ae.GetUplMsgIdHex(i));
824
825
		let cell;
826
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
827
828
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
829
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
830
831
		cell = row.insertCell(-1); cell.textContent = (ae.GetUplMsgBytes(i) / 1024).toFixed(1);
832
833
		cell = row.insertCell(-1);
834
		if (ae.GetUplMsgIdHex(i)) {
835
			cell.innerHTML = "<button data-msgid=\"" + ae.GetUplMsgIdHex(i) + "\" type=\"button\">X</button>";
836
837
			cell.children[0].onclick = function() {
838
				const tr = this.parentElement.parentElement;
839
				ae.Message_Delete(this.getAttribute("data-msgid"), function(error) {
840
					if (!error) tr.remove();
841
					else console.log("Error " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
842
				});
843
			};
844
		}
845
	}
846
}
847
848
function displayOutMsg(num) {
849
	clearDisplay();
850
	document.querySelector("article").scroll(0, 0);
851
	document.querySelector("article").setAttribute("data-msgid", ae.GetOutMsgIdHex(num));
852
853
	document.getElementById("btn_mdele").disabled = false;
854
	document.getElementById("btn_msave").disabled = true;
855
	document.getElementById("btn_reply").disabled = true;
856
857
	document.querySelector("article").children[0].hidden = false;
858
	document.querySelector("article").children[2].hidden = false;
859
860
	document.querySelector("article").children[1].textContent = ae.GetOutMsgSubj(num);
861
	document.querySelector("article").children[2].textContent = ae.GetOutMsgBody(num);
862
863
	document.getElementById("readmsg_dkim").style.visibility    = "hidden";
864
	document.getElementById("readmsg_hdrto").style.visibility   = "visible";
865
	document.getElementById("readmsg_hdrfrom").style.visibility = "visible";
866
	document.getElementById("readmsg_envto").style.visibility   = "visible";
867
	document.getElementById("readmsg_envfrom").style.visibility = "hidden";
868
869
	document.getElementById("readmsg_hdrfrom").textContent = ae.GetOutMsgFrom(num);
870
871
	document.getElementById("readmsg_envto").textContent = ae.GetOutMsgMxDom(num);
872
	document.getElementById("readmsg_hdrto").textContent = ae.GetOutMsgTo(num);
873
874
	const ts = ae.GetOutMsgTime(num);
875
	const tzOs = new Date().getTimezoneOffset();
876
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ");
877
878
	const isInt = ae.GetOutMsgIsInt(num);
879
	document.getElementById("readmsg_ip").style.visibility    = isInt? "hidden" : "visible";
880
	document.getElementById("readmsg_rdns").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
881
	document.getElementById("readmsg_tls").style.visibility   = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
882
	document.getElementById("readmsg_cert").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
883
	document.getElementById("readmsg_greet").style.visibility = isInt? "hidden" : "visible";
884
885
	if (!isInt) {
886
//		const cc = ae.GetExtMsgCountry(num);
887
888
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetOutMsgIp(num);
889
//		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
890
//		document.getElementById("readmsg_tls").children[0].textContent = ae.GetOutMsgTLS(num);
891
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetOutMsgGreet(num);
892
	}
893
894
	let flagText = "";
895
	if (!ae.GetOutMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
896
	if (!ae.GetOutMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
897
	if (ae.GetOutMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
898
	document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
899
}
900
901
function addSent() {
902
	const tbl = document.getElementById("tbl_drbox");
903
	tbl.innerHTML = "";
904
905
	for (let i = 0; i < ae.GetOutMsgCount(); i++) {
906
		const row = tbl.insertRow(-1);
907
		row.setAttribute("data-msgid", ae.GetOutMsgIdHex(i));
908
909
		let cell;
910
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetOutMsgTime(i) * 1000).toISOString().slice(0, 10);
911
		cell = row.insertCell(-1); cell.textContent = ae.GetOutMsgSubj(i);
912
		row.onclick = function() {displayOutMsg(i);};
913
	}
914
}
915
916
function updateAddressCounts() {
917
	document.querySelector("#tbd_accs > tr > td:nth-child(3)").textContent = ae.GetAddressCountNormal();
918
	document.querySelector("#tbd_accs > tr > td:nth-child(4)").textContent = ae.GetAddressCountShield();
919
920
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetLimitNormalA(ae.GetUserLevel())).padStart(ae.GetLimitNormalA(ae.GetUserLevel()) > 9 ? 5 : 1);
921
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetLimitShieldA(ae.GetUserLevel())).padStart(ae.GetLimitShieldA(ae.GetUserLevel()) > 9 ? 5 : 1);
922
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
923
924
	const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
925
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()));
926
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()));
927
}
928
929
function adjustLevel(pubkey, level, c) {
930
	const fs = document.getElementById("tbl_accs");
931
	fs.disabled = true;
932
933
	ae.Account_Update(pubkey, level, function(error) {
934
		fs.disabled = false;
935
936
		if (!error) {
937
			c[4].textContent = level;
938
			c[5].children[0].disabled = (level === 3);
939
			c[6].children[0].disabled = (level === 0);
940
		} else console.log("Error " + error)
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
941
	});
942
}
943
944
function addAccountToTable(i) {
945
	const tblAccs = document.getElementById("tbd_accs");
946
	const row = tblAccs.insertRow(-1);
947
	let cell;
948
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
949
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
950
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
951
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
952
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
953
954
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">+</button>";
955
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) + 1, c);};
956
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 3);
957
958
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">&minus;</button>";
959
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) - 1, c);};
960
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 0);
961
962
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">X</button>";
963
	cell.children[0].onclick = function() {
964
		const tr = this.parentElement.parentElement;
965
		ae.Account_Delete(tr.cells[0].textContent, function(error) {
966
			if (!error) tr.remove();
967
			else console.log("Error " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
968
		});
969
	};
970
}
971
972
function reloadAccount() {
973
	// Limits
974
	const tblLimits = document.getElementById("tbl_limits");
975
	if (ae.IsUserAdmin()) {
976
		for (let i = 0; i < 4; i++) {
977
			tblLimits.rows[i].cells[1].children[0].disabled = false;
978
			tblLimits.rows[i].cells[2].children[0].disabled = false;
979
			tblLimits.rows[i].cells[3].children[0].disabled = false;
980
981
			tblLimits.rows[i].cells[1].children[0].value = ae.GetLimitStorage(i) + 1;
982
			tblLimits.rows[i].cells[2].children[0].value = ae.GetLimitNormalA(i);
983
			tblLimits.rows[i].cells[3].children[0].value = ae.GetLimitShieldA(i);
984
		}
985
	} else {
986
		const lvl = ae.GetUserLevel();
987
		tblLimits.rows[lvl].cells[1].children[0].value = ae.GetLimitStorage(lvl) + 1;
988
		tblLimits.rows[lvl].cells[2].children[0].value = ae.GetLimitNormalA(lvl);
989
		tblLimits.rows[lvl].cells[3].children[0].value = ae.GetLimitShieldA(lvl);
990
	}
991
992
	// Accounts
993
	const tblAccs = document.getElementById("tbd_accs");
994
995
	// All: Our account
996
	const row = tblAccs.insertRow(-1);
997
	let cell;
998
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
999
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1048576); // MiB
1000
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
1001
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
1002
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
1003
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\" disabled=\"disabled\">+</button>";
1004
1005
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_lowme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">&minus;</button>";
1006
	cell.children[0].onclick = function() {
1007
		const newLevel = parseInt(row.cells[4].textContent, 10) - 1;
1008
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(error) {
1009
			if (!error) {
1010
				row.cells[4].textContent = newLevel;
1011
				if (newLevel === 0) {
1012
					document.getElementById("btn_lowme").disabled = true;
1013
					document.getElementById("chk_lowme").disabled = true;
1014
				}
1015
			}
1016
			else console.log("Error " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1017
		});
1018
	};
1019
1020
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_delme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">X</button>";
1021
	cell.children[0].onclick = function() {
1022
		ae.Account_Delete(ae.GetUserPkHex(), function(error) {
1023
			if (!error) {
1024
				row.remove();
1025
				document.getElementById("chk_delme").disabled = true;
1026
			} else console.log("Error " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1027
		});
1028
	};
1029
1030
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
1031
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
1032
	document.getElementById("chk_lowme").disabled = (ae.GetUserLevel() === 0);
1033
1034
	// Contacts
1035
	for (let i = 0; i < ae.GetContactCount(); i++) {
1036
		addContact(
1037
			ae.GetContactMail(i),
1038
			ae.GetContactName(i),
1039
			ae.GetContactNote(i)
1040
		);
1041
	}
1042
1043
	refreshContactList();
1044
1045
	// Addresses
1046
	for (let i = 0; i < ae.GetAddressCount(); i++) {
1047
		addAddress(i);
1048
	}
1049
1050
	updateAddressCounts();
1051
	addMessages();
1052
	addUploads();
1053
	addSent();
1054
1055
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1056
}
1057
1058
function deleteAddress(addr) {
1059
	let btns = document.querySelectorAll("#tbl_addrs button");
1060
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
1061
1062
	let addressToDelete = -1;
1063
1064
	for (let i = 0; i < ae.GetAddressCount(); i++) {
1065
		if (addr === ae.GetAddress(i)) {
1066
			addressToDelete = i;
1067
			break;
1068
		}
1069
	}
1070
1071
	if (addressToDelete === -1) return;
1072
1073
	ae.Address_Delete(addressToDelete, function(error1) {
1074
		if (!error1) {
1075
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
1076
			document.getElementById("write_from").remove(addressToDelete);
1077
			updateAddressCounts();
1078
1079
			const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
1080
			document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() > ae.GetLimitNormalA(ae.GetUserLevel()));
1081
			document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() > ae.GetLimitShieldA(ae.GetUserLevel()));
1082
1083
			ae.Private_Update(function(error2) {
1084
				if (error2) console.log("Failed to update the Private field: " + error2);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1085
1086
				btns = document.querySelectorAll("#tbl_addrs button");
1087
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
1088
			});
1089
		} else {
1090
			console.log("Failed to delete address: " + error1);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1091
1092
			btns = document.querySelectorAll("#tbl_addrs button");
1093
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
1094
		}
1095
	});
1096
}
1097
1098 View Code Duplication
function shieldMix(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1099
	let newAddr = "";
1100
1101
	for (let i = 0; i < 16; i++) {
1102
		switch (addr.charAt(i)) {
1103
			case '1':
1104
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
1105
				break;
1106
			case '0':
1107
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
1108
				break;
1109
			case 'w':
1110
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
1111
				break;
1112
			default:
1113
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
1114
		}
1115
	}
1116
1117
	return newAddr;
1118
}
1119
1120
function addAddress(num) {
1121
	const addrTable = document.getElementById("tbl_addrs");
1122
	const row = addrTable.insertRow(-1);
1123
	const cellAddr = row.insertCell(-1);
1124
	const cellChk1 = row.insertCell(-1);
1125
	const cellChk2 = row.insertCell(-1);
1126
	const cellBtnD = row.insertCell(-1);
1127
1128
	cellAddr.textContent = ae.GetAddress(num);
1129
	cellAddr.onclick = function() {
1130
		if (cellAddr.textContent.length === 16)
1131
			navigator.clipboard.writeText(shieldMix(cellAddr.textContent) + "@" + ae.GetDomainEml());
1132
		else
1133
			navigator.clipboard.writeText(cellAddr.textContent + "@" + ae.GetDomainEml());
1134
	};
1135
1136
	cellChk1.innerHTML = ae.GetAddressAccExt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
1137
	cellChk2.innerHTML = ae.GetAddressAccInt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
1138
1139
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1140
	cellBtnD.onclick = function() {deleteAddress(cellAddr.textContent);};
1141
1142
	const opt = document.createElement("option");
1143
	opt.value = cellAddr.textContent;
1144
	opt.textContent = cellAddr.textContent + "@" + ae.GetDomainEml();
1145
	document.getElementById("write_from").appendChild(opt);
1146
}
1147
1148
document.getElementById("btn_dele").onclick = function() {
1149
	this.blur();
1150
1151
	if (tab === TAB_WRITE) {
1152
		tabs[tab].cur = 0;
1153
		updateTab();
1154
1155
		document.querySelector("#write2_pkey > input").value = "";
1156
1157
		document.getElementById("write_recv").value = "";
1158
		document.getElementById("write_subj").value = "";
1159
		document.getElementById("write_body").value = "";
1160
1161
		document.getElementById("write_recv").readOnly = false;
1162
		document.getElementById("write_subj").readOnly = false;
1163
		document.getElementById("write_subj").setAttribute("data-replyid", "");
1164
1165
		document.getElementById("write_recv").focus();
1166
	}
1167
};
1168
1169
document.getElementById("btn_updt").onclick = function() {
1170
	const btn = this;
1171
	btn.disabled = true;
1172
	btn.blur();
1173
1174
	if (tab === TAB_INBOX) {
1175
		document.getElementById("tbl_inbox").style.opacity = 0.5;
1176
1177
		ae.Message_Browse(true, false, function(error) {
1178
			document.getElementById("tbl_inbox").style.opacity = 1;
1179
1180
			if (!error) {
1181
				addMessages();
1182
				addUploads();
1183
				btn.disabled = false;
1184
			} else {
1185
				console.log("Failed to refresh: " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1186
				btn.disabled = false;
1187
			}
1188
		});
1189
	}
1190
};
1191
1192
document.getElementById("btn_mdele").onclick = function() {
1193
	const btn = this;
1194
	btn.blur();
1195
	btn.disabled = true;
1196
1197
	const delId = document.querySelector("article").getAttribute("data-msgid");
1198
	if (!delId) return;
1199
1200
	ae.Message_Delete(delId, function(error) {
1201
		if (!error) {
1202
			["tbl_inbox", "tbl_drbox", "tbd_uploads"].forEach(function(tbl_name) {
1203
				const tbl = document.getElementById(tbl_name);
1204
				for (let i = 0; i < tbl.rows.length; i++) {if (tbl.rows[i].getAttribute("data-msgid") === delId) tbl.deleteRow(i);}
1205
			});
1206
1207
			addMessages();
1208
			addUploads();
1209
			addSent();
1210
		} else btn.disabled = false; // TODO display error
1211
	});
1212
};
1213
1214
function refreshContactList() {
1215
	const lst = document.getElementById("contact_emails");
1216
	lst.innerHTML = "";
1217
1218
	for (let i = 0; i < ae.GetContactCount(); i++) {
1219
		const el = document.createElement("option");
1220
		el.value = ae.GetContactMail(i);
1221
		lst.appendChild(el);
1222
	}
1223
1224
	if (ae.IsUserAdmin()) {
1225
		const el = document.createElement("option");
1226
		el.value = "public";
1227
		lst.appendChild(el);
1228
	}
1229
}
1230
1231
function addContact(mail, name, note) {
1232
	const tbl = document.getElementById("tbl_ctact");
1233
	const row = tbl.insertRow(-1);
1234
	const cellMail = row.insertCell(-1);
1235
	const cellName = row.insertCell(-1);
1236
	const cellNote = row.insertCell(-1);
1237
	const cellBtnD = row.insertCell(-1);
1238
1239
	cellMail.autocapitalize = "off";
1240
	cellMail.spellcheck = false;
1241
	cellMail.inputMode = "email";
1242
1243
	cellName.autocapitalize = "words";
1244
	cellName.spellcheck = false;
1245
1246
	cellNote.autocapitalize = "off";
1247
	cellNote.spellcheck = false;
1248
1249
	cellMail.textContent = mail;
1250
	cellName.textContent = name;
1251
	cellNote.textContent = note;
1252
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1253
1254
	cellMail.contentEditable = true;
1255
	cellName.contentEditable = true;
1256
	cellNote.contentEditable = true;
1257
1258
	cellBtnD.onclick = function() {row.remove();};
1259
}
1260
1261
document.getElementById("btn_newcontact").onclick = function() {
1262
	addContact("", "", "");
1263
};
1264
1265
document.getElementById("btn_savecontacts").onclick = function() {
1266
	while (ae.GetContactCount() > 0) {
1267
		ae.DeleteContact(0);
1268
	}
1269
1270
	for (const row of document.getElementById("tbl_ctact").rows) {
1271
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1272
	}
1273
1274
	refreshContactList();
1275
1276
	const btn = this;
1277
	btn.disabled = true;
1278
1279
	ae.Private_Update(function(error) {
1280
		btn.disabled = false;
1281
1282
		if (error) {
1283
			console.log("Failed contacts update: " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1284
		}
1285
	});
1286
};
1287
1288
function writeVerify() {
1289
	if (
1290
	   !document.getElementById("write_recv").reportValidity()
1291
	|| !document.getElementById("write_subj").reportValidity()
1292
	|| !document.getElementById("write_body").reportValidity()
1293
	) {tabs[TAB_WRITE].cur = 0; return;}
1294
1295
	document.getElementById("div_write_1").hidden = true;
1296
	document.getElementById("div_write_2").hidden = false;
1297
1298
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1299
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1300
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1301
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1302
1303
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1304
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
1305
		document.getElementById("write2_pkey").hidden = true;
1306
	} else {
1307
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1308
		document.getElementById("write2_pkey").hidden = (document.getElementById("write_recv").value === "public");
1309
	}
1310
1311
	document.querySelector("#write2_send > button").disabled = false;
1312
	document.getElementById("write2_btntxt").textContent = (document.getElementById("write_recv").value === "public") ? "Make" : "Send to";
1313
}
1314
1315
function updateTab() {
1316
	switch (tab) {
1317
		case TAB_INBOX:
1318
			addMessages();
1319
		break;
1320
1321
		case TAB_DRBOX:
1322
			addSent();
1323
		break;
1324
1325
		case TAB_WRITE:
1326
			if (tabs[tab].cur === 0) {
1327
				document.getElementById("div_write_1").hidden = false;
1328
				document.getElementById("div_write_2").hidden = true;
1329
				document.getElementById("write_body").focus();
1330
			} else {
1331
				writeVerify();
1332
			}
1333
		break;
1334
1335
		case TAB_NOTES:
1336
			for (let i = 0; i <= tabs[tab].max; i++) {
1337
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
1338
			}
1339
		break;
1340
1341
		case TAB_TOOLS:
1342
			for (let i = 0; i <= tabs[tab].max; i++) {
1343
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1344
			}
1345
		break;
1346
	}
1347
1348
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1349
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1350
}
1351
1352
document.getElementById("btn_left").onclick = function() {
1353
	tabs[tab].cur--;
1354
	if (tabs[tab].cur === 0) this.disabled = true;
1355
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
1356
	updateTab();
1357
	this.blur();
1358
};
1359
1360
document.getElementById("btn_rght").onclick = function() {
1361
	tabs[tab].cur++;
1362
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
1363
	document.getElementById("btn_left").disabled = false;
1364
	updateTab();
1365
	this.blur();
1366
};
1367
1368
const buttons = document.querySelectorAll("#main1 > .top > button");
1369
for (let i = 0; i < buttons.length; i++) {
1370
	buttons[i].onclick = function() {
1371
		tab = i;
1372
1373
		for (let j = 0; j < buttons.length; j++) {
1374
			document.querySelectorAll("#main1 > .mid > div")[j].hidden = (tab !== j);
1375
			buttons[j].disabled = (tab === j);
1376
		}
1377
1378
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
0 ignored issues
show
Bug introduced by
The variable tab is changed as part of the for loop for example by i on line 1371. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1379
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1380
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1381
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1382
1383
		updateTab();
1384
	};
1385
}
1386
1387
function addressCreate(addr) {
1388
	document.getElementById("btn_address_create_normal").disabled = true;
1389
	document.getElementById("btn_address_create_shield").disabled = true;
1390
1391
	ae.Address_Create(addr, function(error1) {
1392
		if (!error1) {
1393
			ae.Private_Update(function(error2) {
1394
				updateAddressCounts();
1395
1396
				if (!error2) {
1397
					addAddress(ae.GetAddressCount() - 1);
1398
					if (addr !== "SHIELD") {
1399
						document.getElementById("txt_address_create_normal").value = "";
1400
						document.getElementById("txt_address_create_normal").focus();
1401
					}
1402
				} else {
1403
					console.log("Failed to update the Private field: " + error2);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1404
				}
1405
			});
1406
		} else {
1407
			console.log("Failed to add address: " + error1);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1408
			updateAddressCounts();
1409
		}
1410
	});
1411
}
1412
1413
document.getElementById("btn_address_create_normal").onclick = function() {
1414
	if (ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1415
1416
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1417
	if (!txtNewAddr.reportValidity()) return;
1418
1419
	addressCreate(txtNewAddr.value);
1420
};
1421
1422
document.getElementById("btn_address_create_shield").onclick = function() {
1423
	if (ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1424
1425
	addressCreate("SHIELD");
1426
};
1427
1428
document.getElementById("btn_address_update").onclick = function() {
1429
	const btn = this;
1430
	btn.disabled = true;
1431
1432
	const rows = document.getElementById("tbl_addrs").rows;
1433
1434
	for (let i = 0; i < rows.length; i++) {
1435
		ae.SetAddressAccExt(i, rows[i].getElementsByTagName("input")[0].checked);
1436
		ae.SetAddressAccInt(i, rows[i].getElementsByTagName("input")[1].checked);
1437
	}
1438
1439
	ae.Address_Update(function(error) {
1440
		if (error) console.log("Address/Update failed: " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1441
		btn.disabled = false;
1442
	});
1443
};
1444
1445
1446
document.getElementById("txt_reg").onkeyup = function(event) {
1447
	if (event.key === "Enter") {
1448
		event.preventDefault();
1449
		document.getElementById("btn_reg").click();
1450
	}
1451
};
1452
1453
document.getElementById("btn_reg").onclick = function() {
1454
	const btn = document.getElementById("btn_reg");
1455
	const txt = document.getElementById("txt_reg");
1456
	if (!txt.reportValidity()) return;
1457
	btn.disabled = true;
1458
1459
	ae.Account_Create(txt.value, function(error) {
1460
		if (!error) {
1461
			addAccountToTable(ae.Admin_GetUserCount() - 1);
1462
			txt.value = "";
1463
		} // else TODO
1464
1465
		btn.disabled = false;
1466
	});
1467
};
1468
1469
document.getElementById("chk_delme").onclick = function() {document.getElementById("btn_delme").disabled = !this.checked;};
1470
document.getElementById("chk_lowme").onclick = function() {document.getElementById("btn_lowme").disabled = !this.checked;};
1471
1472
document.getElementById("btn_notepad_saveupl").onclick = function() {
1473
	const np = document.getElementById("txt_notepad");
1474
	np.disabled = true;
1475
1476
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1477
	if (!fname.endsWith(".txt")) fname += ".txt";
1478
1479
	ae.Message_Upload(fname, np.value, function(error) {
1480
		if (!error) {
1481
			np.value = "";
1482
			addUploads();
1483
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1484
		} else {
1485
			console.log("Failed to add text: " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1486
		}
1487
1488
		np.disabled = false;
1489
	});
1490
};
1491
1492
document.getElementById("btn_upload").onclick = function() {
1493
	const btn = this;
1494
	const fileSelector = document.createElement("input");
1495
	fileSelector.type = "file";
1496
	fileSelector.click();
1497
1498
	fileSelector.onchange = function() {
1499
		btn.disabled = true;
1500
1501
		const reader = new FileReader();
1502
		reader.onload = function() {
1503
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(error) {
1504
				if (!error) {
1505
					addUploads();
1506
					document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1507
				} else {
1508
					console.log("Failed upload: " + error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1509
				}
1510
1511
				btn.disabled = false;
1512
			});
1513
		};
1514
1515
		reader.readAsArrayBuffer(fileSelector.files[0]);
1516
	};
1517
};
1518
1519
document.getElementById("btn_pg").onclick = function() {
1520
	localStorage.greeting = document.getElementById("txt_pg").value;
1521
};
1522
1523
document.querySelector("#write2_send > button").onclick = function() {
1524
	const btn = this;
1525
	btn.disabled = true;
1526
1527
	// Public announcement
1528
	if (document.getElementById("write2_recv").textContent === "public") {
1529
		ae.Message_Public(document.getElementById("write_subj").value, document.getElementById("write_body").value, function(error) {
1530
			if (!error) {
1531
				document.getElementById("write2_btntxt").textContent = "Announced to";
1532
				document.getElementById("write_recv").value = "";
1533
				document.getElementById("write_subj").value = "";
1534
				document.getElementById("write_body").value = "";
1535
			} else {
1536
				// TODO display error
1537
				document.getElementById("write2_btntxt").textContent = "Retry making";
1538
				btn.disabled = false;
1539
			}
1540
		});
1541
1542
		return;
1543
	}
1544
1545
	// Email or internal message
1546
	document.getElementById("write2_btntxt").textContent = "Sending to";
1547
1548
	ae.Message_Create(
1549
		document.getElementById("write_subj").value,
1550
		document.getElementById("write_body").value,
1551
		document.getElementById("write_from").value,
1552
		document.getElementById("write_recv").value,
1553
		document.getElementById("write_subj").getAttribute("data-replyid"),
1554
		(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_base64(document.querySelector("#write2_pkey > input").value, sodium.base64_variants.ORIGINAL_NO_PADDING),
1555
		function(error) {
1556
			if (!error) {
1557
				document.getElementById("write2_btntxt").textContent = "Delivered to";
1558
				document.getElementById("write_recv").value = "";
1559
				document.getElementById("write_subj").value = "";
1560
				document.getElementById("write_body").value = "";
1561
			} else {
1562
				errorDialog(error);
1563
1564
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1565
				btn.disabled = false;
1566
			}
1567
		}
1568
	);
1569
};
1570
1571
document.getElementById("txt_skey").onfocus = function() {
1572
	document.getElementById("greeting").textContent = localStorage.greeting;
1573
};
1574
1575
document.getElementById("txt_skey").onkeyup = function(event) {
1576
	if (event.key === "Enter") {
1577
		event.preventDefault();
1578
		document.getElementById("btn_enter").click();
1579
	}
1580
};
1581
1582
document.getElementById("btn_enter").onclick = function() {
1583
	const txtSkey = document.getElementById("txt_skey");
1584
1585
	if (txtSkey.value === "") {
1586
		ae.Reset();
1587
		document.getElementById("greeting").textContent = "Data cleared";
1588
		return;
1589
	}
1590
1591
	if (!txtSkey.reportValidity()) return;
1592
1593
	const btn = this;
1594
	btn.disabled = true;
1595
1596
	document.getElementById("txt_skey").disabled = true;
1597
	document.getElementById("txt_skey").style.background = "#233";
1598
1599
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
1600
		if (successSetKeys) {
1601
			document.body.style.cursor = "wait";
1602
1603
			ae.Message_Browse(false, true, function(errorBrowse) {
1604
				document.body.style.cursor = "auto";
1605
1606
				if (!errorBrowse) {
1607
					txtSkey.value = "";
1608
					document.getElementById("div_begin").hidden = true;
1609
					document.getElementById("div_main").hidden = false;
1610
					reloadAccount();
1611
1612
					if (ae.IsUserAdmin()) {
1613
						ae.Account_Browse(function(errorAcc) {
1614
							if (!errorAcc) {
1615
								for (let i = 0; i < ae.Admin_GetUserCount(); i++) {addAccountToTable(i);}
1616
							} else {
1617
								console.log("Failed to Account_Browse: " + errorAcc);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1618
							}
1619
						});
1620
					}
1621
				} else {
1622
					document.getElementById("txt_skey").disabled = false;
1623
					document.getElementById("txt_skey").style.background = "#466";
1624
					btn.focus();
1625
1626
					document.getElementById("greeting").textContent = "Error " + errorBrowse;
1627
					btn.disabled = false;
1628
				}
1629
			});
1630
		} else {
1631
			document.getElementById("txt_skey").disabled = false;
1632
			document.getElementById("txt_skey").style.background = "#466";
1633
			txtSkey.focus();
1634
1635
			document.getElementById("greeting").textContent = "SetKeys failed";
1636
			btn.disabled = false;
1637
		}
1638
	});
1639
};
1640
1641
});
1642